using System;
using UnityEngine;
using System.Collections.Generic;
using System.Collections;

#pragma warning disable 0108

/// <summary>
/// 提供一个类AssetInfo，用来封装保存Resources的信息，和资源加载的一些方法
/// </summary>
public class AssetInfo
{
    //资源加载成功后的Object对象，加载一次后，以后不再加载，直接用这个对象
    private UnityEngine.Object _Object;
    //资源的类型
    public Type AssetType { get; set; }
    //资源的存储路径
    public string Path { get; set; }
    //引用计数，可以根据计数的多少，决定何时释放资源，次数多的可以留在内存里久一点
    public int RefCount { get; set; }
    //是否已经加载成功
    public bool IsLoaded
    {
        get
        {
            return null != _Object;//资源加载成功后的Object对象"_Object"不为空的时候，表示已经加载成功了
        }
    }

    /// <summary>
    /// 属性方法
    /// 取得：资源加载成功后的Object对象"_Object"
    /// </summary>
    public UnityEngine.Object AssetObject
    {
        get
        {
            if (null == _Object)//资源加载成功后的Object对象"_Object"为空的时候，调用按路径加载资源的方法
            {
                _ResourcesLoad();//按路径加载资源的方法
            }
            return _Object;
        }
    }

    /// <summary>
    /// 协同程序完成加载
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetCoroutineObject(Action<UnityEngine.Object> _loaded)
    {
        while (true)
        {
            yield return null;
            if (null == _Object)// 当_Object为空时
            {
                _ResourcesLoad();// Load一个对象
                yield return null;
            }

            //不等于空的时候
            if (null != _loaded)
                _loaded(_Object);// 将我们的对象用委托上传上去
            yield break;
        }
    }

    /// <summary>
    /// 资源加载的通用主函数
    /// 从Resources文件夹下，按路径加载资源的方法
    /// </summary>
    private void _ResourcesLoad()
    {
        //加载时进行异常捕获
        try
        {
            _Object = Resources.Load(Path);
            if (null == _Object)
                Debug.Log("Resources Load Failure! Path:" + Path);
        }
        catch (Exception e)
        {
            Debug.LogError(e.ToString());
        }
    }

    /// <summary>
    /// 异步加载的方法
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetAsyncObject(Action<UnityEngine.Object> _loaded)
    {
        return GetAsyncObject(_loaded, null);
    }

    /// <summary>
    /// 异步加载的主方法
    /// </summary>
    /// <returns></returns>
    public IEnumerator GetAsyncObject(Action<UnityEngine.Object> _loaded, Action<float> _progress)
    {
        //资源加载成功后的Object对象"_Object"不为空的时候，代表已经加载过了
        if (null != _Object)
        {
            _loaded(_Object);
            yield break;//中断，跳出迭代
        }

        // 通过路径异步加载 
        ResourceRequest _resRequest = Resources.LoadAsync(Path);

        // 如果加载的进度 小于0.9
        while (_resRequest.progress < 0.9)
        {
            if (null != _progress)//如果进度的委托不为空
                _progress(_resRequest.progress);//调用
            yield return null;
        }

        // 如果还没有完成，0.9-1
        while (!_resRequest.isDone)
        {
            if (null != _progress)
                _progress(_resRequest.progress);
            yield return null;
        }

        // ???// 加载完成
        _Object = _resRequest.asset;
        if (null != _loaded)// 如果 Object的委托不为空
            _loaded(_Object);

        yield return _resRequest;
    }
}


// (1) yield return WaitForSeconds(3.0f);    ===> 就是延时一段时间以后继续往下执行
// (2) yield return null;                    ===> 暂停协同程序，下一帧再继续往下执行
// (3) yield break;                          ===> 用来表明迭代结束，跳出迭代


public class ResManager : Singleton_UI<ResManager>
{
    // 一个保存已加载资源的字典
    private Dictionary<string, AssetInfo> dicAssetInfo = null;

    //Resources.Load();//即时加载，我们要实现：（1）load完直接返回object,直接使用它   （2）协同程序的即时加载
    //Resources.LoadAsync();//异步加载

    public override void Init()
    {
        dicAssetInfo = new Dictionary<string, AssetInfo>();
    }

    #region 对应三种加载模式的实例化方法    Load Resources & Instantiate Object
    /// <summary>
    /// Load方法的实例化，通过路径
    /// </summary>
    /// <returns>The instance.</returns>
    public UnityEngine.Object LoadInstance(string _path)
    {
        UnityEngine.Object _obj = Load(_path);
        return Instantiate(_obj);//
    }

    /// <summary>
    /// 通用的实例化的方法，重载函数
    /// </summary>
    private UnityEngine.Object Instantiate(UnityEngine.Object _obj)
    {
        return Instantiate(_obj, null);
    }

    /// <summary>
    /// 通用的实例化的方法，主函数
    /// </summary>
    private UnityEngine.Object Instantiate(UnityEngine.Object _obj, Action<UnityEngine.Object> _loaded)
    {
        UnityEngine.Object _retObj = null;//定义一个返回值类型
        //如果参数 _obj不为空，进行实例化
        if (null != _obj)
        {
            _retObj = MonoBehaviour.Instantiate(_obj);
            //实例化后不为空
            if (null != _retObj)
            {
                //对异步协同程序的处理
                //委托不为空的时候
                if (null != _loaded)
                {
                    //用委托通知，成功了
                    _loaded(_retObj);
                    return null;
                }
                // 返回值
                return _retObj;
            }
            else//抛异常
            {
                Debug.LogError("Error: null Instantiate _retObj.");
            }
        }
        else//抛异常
        {
            Debug.LogError("Error: null Resources Load return _obj.");
        }
        return null;
    }

    /// <summary>
    /// 协程加载的实例化函数
    /// </summary>
    /// <param name="_path">_path.</param>
    public void LoadCoroutineInstance(string _path, Action<UnityEngine.Object> _loaded)
    {
        LoadCoroutine(_path, (_obj) => { Instantiate(_obj, _loaded); });
    }

    /// <summary>
    /// 异步加载的实例化函数
    /// </summary>
    public void LoadAsyncInstance(string _path, Action<UnityEngine.Object> _loaded)
    {
        LoadAsync(_path, (_obj) => { Instantiate(_obj, _loaded); });
    }

    /// <summary>
    /// 异步加载的实例化函数，主函数
    /// </summary>
    public void LoadAsyncInstance(string _path, Action<UnityEngine.Object> _loaded, Action<float> _progress)
    {
        LoadAsync(_path, (_obj) => { Instantiate(_obj, _loaded); }, _progress);
    }
    #endregion


    #region Load函数   Load Resources
    /// <summary>
    /// 通过路径 获取 AssetInfo 信息的Load函数
    /// </summary>
    /// <param name="_path">_path.</param>
    public UnityEngine.Object Load(string _path)
    {
        AssetInfo _assetInfo = GetAssetInfo(_path); //通过路径 获取 AssetInfo 信息
        //判断 _assetInfo是否为空
        if (null != _assetInfo)
            return _assetInfo.AssetObject;// 不为空，返回_assetInfo.AssetObject
        return null;//为空，返回空
    }
    #endregion


    #region Load的协程函数   Coroutine Resources

    /// <summary>
    /// 资源加载的协同程序实现
    /// </summary>
    public void LoadCoroutine(string _path, Action<UnityEngine.Object> _loaded)
    {
        AssetInfo _assetInfo = GetAssetInfo(_path, _loaded);// 获取 AssetInfo 信息
        //判断 _assetInfo是否为空
        // 不为空，通过 AssetInfo类的协程方法加载资源
        if (null != _assetInfo)
            CoroutineController.Instance.StartCoroutine(_assetInfo.GetCoroutineObject(_loaded));
    }
    #endregion


    #region Load的异步加载函数  Load Async Resources
    /// <summary>
    /// 通过异步加载资源 重载函数
    /// </summary>
    public void LoadAsync(string _path, Action<UnityEngine.Object> _loaded)
    {
        LoadAsync(_path, _loaded, null);
    }

    /// <summary>
    /// 通过异步加载资源 主函数
    /// </summary>
    public void LoadAsync(string _path, Action<UnityEngine.Object> _loaded, Action<float> _progress)
    {
        AssetInfo _assetInfo = GetAssetInfo(_path, _loaded);// 获取 AssetInfo 信息
        //判断 _assetInfo是否为空
        // 不为空，通过 AssetInfo类的 异步加载资源方法 进行加载
        if (null != _assetInfo)
            CoroutineController.Instance.StartCoroutine(_assetInfo.GetAsyncObject(_loaded, _progress));
    }
    #endregion


    #region 获取 AssetInfo 信息    Get AssetInfo & Instantiate Object

    /// <summary>
    /// 获取 AssetInfo 信息的重载函数
    /// </summary>
    /// <returns>The asset info.</returns>
    private AssetInfo GetAssetInfo(string _path)
    {
        return GetAssetInfo(_path, null);
    }

    /// <summary>
    /// 获取 AssetInfo 信息 主函数
    /// </summary>
    /// <returns>The asset info.</returns>
    private AssetInfo GetAssetInfo(string _path, Action<UnityEngine.Object> _loaded)
    {
        //判断资源加载的路径是否为空
        if (string.IsNullOrEmpty(_path))
        {
            Debug.LogError("Error: null _path name.");// 输出空的路径名
            if (null != _loaded)// 看_loaded回调是否为空
                _loaded(null);//通知它调用失败
        }
        // 如果 AssetInfo 为空
        AssetInfo _assetInfo = null;
        // 如果已加载资源里没有
        if (!dicAssetInfo.TryGetValue(_path, out _assetInfo))
        {
            // new 一个
            _assetInfo = new AssetInfo();
            _assetInfo.Path = _path;
            // 添加到字典里
            dicAssetInfo.Add(_path, _assetInfo);
        }
        _assetInfo.RefCount++;// 引用计数加1

        return _assetInfo;//返回AssetInfo
    }
    #endregion

}


